    using System;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using DirectShowLib;
using System.Threading;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using System.Reflection;
using Lizk.SimpleHUD;

namespace SharpMedia.MDX
{
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    public class Allocator2 : IVMRSurfaceAllocator9, IVMRImagePresenter9, IDisposable
    {
        // Constants
        private const int E_FAIL = unchecked((int) 0x80004005);
        
        private IntPtr unmanagedSurface = IntPtr.Zero;

        private Vector2 textureSize;
        private Vector2 videoSize;

        private IVMRSurfaceAllocatorNotify9 vmrSurfaceAllocatorNotify = null;

        private RenderTarget2D target;

        // Handy Properties to retrieve the texture, its size and the video size
        public Vector2 VideoSize { get { return videoSize; } }

        private GraphicsDevice device;
        private object allocatorLock = new object();

        private bool hasImage;
        private Texture2D texture;
        public bool Closing = false, Ready = false;





        public Texture2D GetImage()
        {

            Monitor.Enter(allocatorLock);
            
                needTexture = true;

                Monitor.PulseAll(allocatorLock);
        
                if(video.Player.State == Lizk.DShowPlayer.PlayerState.Playing || video.Player.State == Lizk.DShowPlayer.PlayerState.Menu)
                    Monitor.Wait(allocatorLock, 100);
                needTexture = false;

            Monitor.Exit(allocatorLock);
            return texture;
        }

        XNAVideo video;

        public Allocator2(GraphicsDevice graphics, XNAVideo video)
        {
            this.device = graphics;
            this.video = video;
        }

        ~Allocator2()
        {
            Dispose();
        }

        public void Dispose()
        {

            lock (allocatorLock)
            {
                if (target != null)
                {
                    if(!target.IsDisposed)
                        target.Dispose();
                    target = null;
                }
            }
        }

        public void Close()
        {
            lock (allocatorLock)
            {
                Closing = true;
                Monitor.PulseAll(allocatorLock);
            }
        }

       
        public unsafe void CreateSurfaces(VMR9AllocationInfo lpAllocInfo)
        {
            lock (allocatorLock)
            {
                // An offscreen surface must be created
                lpAllocInfo.dwFlags |= VMR9SurfaceAllocationFlags.OffscreenSurface |
                                       VMR9SurfaceAllocationFlags.DXVATarget;

                target = new RenderTarget2D(device, lpAllocInfo.dwWidth, lpAllocInfo.dwHeight, 1, device.PresentationParameters.BackBufferFormat);
                // And get it unmanaged pointer
                Pointer p = (Pointer)typeof(RenderTarget).GetField("pRenderTargetSurface", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(target);

                unmanagedSurface = new IntPtr(Pointer.Unbox(p));

                video.video.Updater.SetSourceSize(new SizeS(lpAllocInfo.dwWidth, lpAllocInfo.dwHeight));


            }

        }


        #region Members of IVMRSurfaceAllocator9
       
        public int InitializeDevice(IntPtr dwUserID, ref VMR9AllocationInfo lpAllocInfo, ref int lpNumBuffers)
        {
            lock (allocatorLock)
            {
                try
                {
                    videoSize = new Vector2(lpAllocInfo.dwWidth, lpAllocInfo.dwHeight);
                    textureSize = new Vector2(lpAllocInfo.szNativeSize.Width, lpAllocInfo.szNativeSize.Height);
                    // Just in case
                    Dispose();
                    CreateSurfaces(lpAllocInfo);

                    // This allocator only support 1 buffer.
                    // Notify the VMR9 filter
                    lpNumBuffers = 1;
                }


                catch
                {
                    // Or else, notify a more general error
                    return E_FAIL;
                }
            }
            // This allocation is a success
            return 0;
        }

 

        public int TerminateDevice(IntPtr dwID)
        {
            Dispose();
            return 0;
        }

        public int GetSurface(IntPtr dwUserID, int SurfaceIndex, int SurfaceFlags, out IntPtr lplpSurface)
        {
            lplpSurface = IntPtr.Zero;

            if (SurfaceIndex >= 1 || Closing)
                return E_FAIL;

            lock (allocatorLock)
            {
                lplpSurface = unmanagedSurface;
                Marshal.AddRef(lplpSurface);
                return 0;
            }
        }

        public unsafe int AdviseNotify(IVMRSurfaceAllocatorNotify9 lpIVMRSurfAllocNotify)
        {
            lock (allocatorLock)
            {
                vmrSurfaceAllocatorNotify = lpIVMRSurfAllocNotify;

                object comPointer = (Pointer)typeof(GraphicsDevice).GetField("pComPtr", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(device);
                //object o = typeof(Pointer).GetField("_ptr", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(comPointer);

                IntPtr unmanagedDevice = new IntPtr(Pointer.Unbox(comPointer));
                // Give our Direct3D device to the VMR9 filter
                IntPtr hMonitor = device.CreationParameters.Adapter.MonitorHandle;

                return vmrSurfaceAllocatorNotify.SetD3DDevice(unmanagedDevice, hMonitor);
            }
        }

        #endregion

        #region Membres de IVMRImagePresenter9
        
        public int StartPresenting(IntPtr dwUserID)
        {
            if (device == null)
                return E_FAIL;


            return 0;
        }

        public int StopPresenting(IntPtr dwUserID)
        {
            return 0;
        }
        
        
        
        private bool wait, needTexture;

        public bool Wait
        {
            get { return wait; }
            set { wait = value; lock (allocatorLock) { if (hasImage) Monitor.PulseAll(allocatorLock); } }
        }
        
        public int PresentImage(IntPtr dwUserID, ref VMR9PresentationInfo lpPresInfo)
        {
            Monitor.Enter(allocatorLock);
            hasImage = true;
            while (!Closing && !Wait && !needTexture)
            {
                Monitor.Wait(allocatorLock);
            }
            if (Closing || Wait)
            {
                Monitor.Exit(allocatorLock);
                return 0;
            }
            
            device.SetRenderTarget(0, target);
            device.SetRenderTarget(0, null);
            texture = target.GetTexture();

            hasImage = false;
            Monitor.PulseAll(allocatorLock);
            Monitor.Exit(allocatorLock);

            // This presentation is a success
            return 0;
        }

        #endregion
    }
}